type TmergeObject<T> = undefined | null | T;
interface _CU_ {
    // TODO merge方法的返回值是每一个参数类型的交叉类型
    /**
     * 合并对象
     * @param objs 待合并对象
     * @returns 合并后新对象
     */
    merge<A extends object>(obj1?: TmergeObject<A>): A;
    merge<A extends object, B extends object>(obj1: TmergeObject<A>, obj2: TmergeObject<B>): A & B;
    merge<A extends object, B extends object, C extends object>(obj1: TmergeObject<A>, obj2: TmergeObject<B>, obj3: TmergeObject<C>): A & B & C;
    merge<A extends object, B extends object, C extends object, D extends object>(obj1: TmergeObject<A>, obj2: TmergeObject<B>, obj3: TmergeObject<C>, obj4: TmergeObject<D>): A & B & C & D;
}
{
    const set_or_merge = U.F.overloading('Utils-merge-set_or_merge',
        {
            types: [[Etype.obj, Etype.array], U.F.argType.propertyKey, U.F.argType.nest, U.F.argType.nest],
            fn: (result: any, key: any, value: any, result_value: any) => {// 待合并对象中存放的是数组或对象或集合，则将合并结果用来替换存放的值。
                result[key] = merge(result_value, value);
            }
        },
        {
            types: [[Etype.obj, Etype.array], U.F.argType.propertyKey, U.F.argType.nest, U.F.argType.any],
            fn: (result: any, key: any, value: any, result_value: any) => {// 结果中存放的是其它类型则用新值直接替换
                result[key] = value;
            }
        },



        {
            types: [Etype.Map, U.F.argType.propertyKey, U.F.argType.nest, U.F.argType.nest],
            fn: (result: Map<any, any>, key: any, value: any, result_value: any) => {// 待合并对象中存放的是数组或对象或集合，则将合并结果用来替换存放的值。
                result.set(key, merge(result_value, value));
            }
        },
        {
            types: [Etype.Map, U.F.argType.propertyKey, U.F.argType.nest, U.F.argType.any],
            fn: (result: Map<any, any>, key: any, value: any, result_value: any) => {// 结果中存放的是其它类型则用新值直接替换
                result.set(key, value);
            }
        },
    );
    const setValue = U.F.overloading('Utils-merge-setValue',
        {
            types: [[Etype.obj, Etype.array, Etype.Map], U.F.argType.propertyKey, U.F.argType.nest],
            fn: (result: any, key: any, value: any) => {// 待合并对象中存放的是数组或对象或集合，则需要根据结果中存放的数据进行不同操作。
                set_or_merge(result, key, value, result[key]);
            }
        },
        {
            types: [[Etype.obj, Etype.array, Etype.Map], U.F.argType.propertyKey, U.F.argType.any],
            fn: (result: any, key: any, value: any) => {// 待合并对象中存放的是其它值类型，则替换结果。
                result[key] = value;
            }
        },
    );
    const mergeToObj = (result: any, key: any, value: any) => {
        if (Object.prototype.hasOwnProperty.call(result, key)) {// 结果中已经存在
            setValue(result, key, value);// 合并属性
        } else {// 结果中不存在，则直接存入副本。
            result[key] = U.clone(value);
        }
    };
    const mergetToMap = (result: any, key: any, value: any) => {
        if (result.has(key)) {// 结果中已经存在
            setValue(result, key, value);// 合并属性
        } else {// 结果中不存在，则直接存入副本。
            result.set(key, U.clone(value));
        }
    };
    // 辅助方法：将第二个值合并到第一个值中， 此处 第一个值 一定是 嵌套类型
    const mergeTo = U.F.overloading('Utils-merge-mergeTo',

        // ========= merge to array or obj ========= //
        {
            types: [[Etype.obj, Etype.array], [Etype.obj, Etype.array]],
            fn: (result: any, obj: any) => {
                for (const key in obj) {
                    if (Object.prototype.hasOwnProperty.call(obj, key)) {
                        const value = obj[key];
                        mergeToObj(result, key, value);
                    }
                }
            }
        },
        {
            types: [[Etype.obj, Etype.array], Etype.Map],
            fn: (result: any, obj: any) => {
                for (const [key, value] of obj) {
                    mergeToObj(result, key, value);
                }
            }
        },
        {
            types: [Etype.obj, Etype.Set],
            fn: (result: any, obj: any) => {
                let key = 0;
                for (const value of obj) {
                    mergeToObj(result, key, value);
                    key++;
                }
            }
        },
        {
            types: [Etype.array, Etype.Set],
            fn: (result: any, obj: any) => {
                for (const value of obj) {
                    result.push(U.clone(value));
                }
            }
        },



        // ========= merge to Map ========= //
        {
            types: [Etype.Map, [Etype.obj, Etype.array]],
            fn: (result: any, obj: any) => {
                for (const key in obj) {
                    if (Object.prototype.hasOwnProperty.call(obj, key)) {
                        const value = obj[key];
                        mergetToMap(result, key, value);
                    }
                }
            }
        },
        {
            types: [Etype.Map, Etype.Map],
            fn: (result: any, obj: any) => {
                for (const [key, value] of obj) {
                    mergetToMap(result, key, value);
                }
            }
        },
        {
            types: [Etype.Map, Etype.Set],
            fn: (result: any, obj: any) => {
                let key = 0;
                for (const value of obj) {
                    mergetToMap(result, key, value);
                    key++;
                }
            }
        },



        // ========= merge to Set ========= //
        {
            types: [Etype.Set, [Etype.obj, Etype.array]],
            fn: (result: any, obj: any) => {
                for (const key in obj) {
                    if (Object.prototype.hasOwnProperty.call(obj, key)) {
                        const value = obj[key];
                        result.add(U.clone(value));
                    }
                }
            }
        },
        {
            types: [Etype.Set, Etype.Map],
            fn: (result: any, obj: any) => {
                for (const [key, value] of obj) {
                    result.add(U.clone(value));
                }
            }
        },
        {
            types: [Etype.Set, Etype.Set],
            fn: (result: any, obj: any) => {
                for (const value of obj) {
                    result.add(U.clone(value));
                }
            }
        },



        {
            // 待合并项 是 特殊值类型 => 无需处理
            types: [U.F.argType.nest, [Etype.undefined, Etype.null, Etype.DEFAULT, Etype.NULLOBJ]],
            fn: U.F.fn.void
        },
        {
            // 待合并项 是 其它类型 => 不支持
            // 已有结果项，不报错，跳过处理即可。
            types: [U.F.argType.nest, U.F.argType.any],
            fn: U.F.fn.void
        },
    );
    const merge = U.F.overloading('Utils-merge',
        {
            // 单值合并： 特殊值类型 => 返回空对象
            types: [[U.F.argType.noArgs, Etype.undefined, Etype.null, Etype.DEFAULT, Etype.NULLOBJ]],
            fn: U.F.fn.emptyObj
        },
        {
            // 单值合并： 嵌套类型 => 返回克隆对象 
            types: [U.F.argType.nest],
            fn: (obj: any) => { return U.clone(obj); }
        },
        {
            // 多值合并： 逐一进行合并
            types: [U.F.argType.rest],
            fn: (obj: any, ...objs: Array<any>) => {
                let result = merge(obj);// 此处 result一定是 嵌套类型
                objs.forEach(objX => { mergeTo(result, objX); });// 调用 双值合并 方法，根据类型做不同处理。
                return result;
            }
        },
        {
            // 单值合并： 其它类型 => 不支持
            types: [Etype.symbol],
            fn: (obj: any) => { throw new IllegalArgumentError(`this type[${U.typeof(obj, false)}] of value[${Symbol.name}(${obj.description})] cannot be used for merging`); }
        },
        {
            // 单值合并： 其它类型 => 不支持
            types: [U.F.argType.any],
            fn: (obj: any) => { throw new IllegalArgumentError(`this type[${U.typeof(obj, false)}] of value[${obj}] cannot be used for merging`); }
        }
    );

    Object.defineProperty(_CU_.prototype, 'merge', {
        writable: false,
        configurable: false,
        enumerable: false,
        value: merge
    });
}
